/**
 * Copyright 2018 orivil.com. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package n_spider

import (
	"bytes"
	"encoding/json"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"image"
	"image/jpeg"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"sync"
	"time"
)

// 最大图片 600KB
const MaxImageSize = 600 << 10

type Auth struct {
	Headers []*Header
	Wait    time.Duration // 等待时间
	locker  sync.RWMutex
	index   int
}

func (a *Auth) rangeHeader() *Header {
	a.locker.Lock()
	defer a.locker.Unlock()
	header := a.Headers[a.index]
	a.index++
	if a.index == len(a.Headers) {
		a.index = 0
	}
	return header
}

func (a *Auth) Header() *Header {
	header := a.rangeHeader()
	if a.Wait != 0 {
		time.Sleep(a.Wait / time.Duration(len(a.Headers)))
	}
	return header
}

type Header struct {
	Name    string
	Charset string
	Proxy   string
	Values  string
}

/**
   将 headers 数据加入 request 中
   如:
	Accept: application/json, text/plain
	Accept-Encoding: gzip, deflate, br
	Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
	Authorization: Bearer d9d72c6e62164a30be457d93b055a0de
	Cache-Control: no-cache
	Connection: keep-alive
	Cookie: aliyungf_tc=AQAAAA2Frx+ykQAAw4NQfTaWwsd/mnj8; member_token=d9d72c6e62164a30be457d93b055a0de; ZAID=78236153-a4b7-eb68-c400-78c36bb62d6d; __fvmp=1; __trade_type=jsapi; Hm_lvt_d5e4918c33053db244a223b60906a4d3=1534297329,1534769483,1534819373,1534904346; Hm_lpvt_d5e4918c33053db244a223b60906a4d3=1534927891
	Host: c16050.818tu.com
	Pragma: no-cache
	Referer: https://c16050.818tu.com/m/read/4274
	User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.884.400 QQBrowser/9.0.2524.400
	x-channel-id: 16050
*/
func (h *Header) initRequest(req *http.Request) (err error) {
	kvs := strings.Split(h.Values, "\n")
	for _, kv := range kvs {
		pairs := strings.Split(kv, ":")
		if len(pairs) != 2 {
			return fmt.Errorf("headers 数据格式错误: %s", h.Name)
		} else {
			req.Header.Set(pairs[0], pairs[1])
		}
	}
	return nil
}

func (h *Header) initClient() (client *http.Client, err error) {
	if h.Proxy != "" {
		u, err := url.Parse(h.Proxy)
		if err != nil {
			return nil, err
		}
		client = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(u)}}
		return client, nil
	} else {
		return http.DefaultClient, nil
	}
}

func (h *Header) GetFile(uri string) (file []byte, err error) {
	req, err := http.NewRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	h.initRequest(req)
	client, err := h.initClient()
	if err != nil {
		return nil, err
	}
	try := 10
	var res *http.Response
	for i := 0; i < try; i++ {
		res, err = client.Do(req)
		if err != nil {
			continue
		} else {
			break
		}
	}
	if res == nil {
		return nil, err
	}
	defer res.Body.Close()
	buf := bytes.NewBuffer(nil)
	_, err = io.Copy(buf, res.Body)
	if err != nil {
		return nil, err
	} else {
		return buf.Bytes(), nil
	}
}

func (h *Header) GetContent(uri string) (content []byte, err error) {
	defer func() {
		if len(content) != 0 {
			if h.Charset == "GBK" {
				content, err = gbkToUtf8(content)
			}
		}
	}()
	req, e := http.NewRequest("GET", uri, nil)
	if e != nil {
		return nil, e
	}
	h.initRequest(req)
	client, err := h.initClient()
	if err != nil {
		return nil, err
	}
	try := 10
	var res *http.Response
	for i := 0; i < try; i++ {
		res, e = client.Do(req)
		if e != nil {
			continue
		} else {
			break
		}
	}
	if res == nil {
		return nil, e
	}
	defer res.Body.Close()
	buf := bytes.NewBuffer(nil)
	_, e = io.Copy(buf, res.Body)
	if e != nil {
		return nil, e
	}
	data := buf.Bytes()
	if strings.Contains(res.Header.Get("Content-Type"), "application/json") || bytes.HasPrefix(data, []byte("{")) {
		var m = make(map[string]interface{}, 20)
		err = json.Unmarshal(data, &m)
		if err != nil {
			return nil, err
		} else {
			return json.Marshal(m)
		}
	} else {
		return data, nil
	}
}

type ImageStorage interface {
	IsExist(ptID, ptBookID int) bool
	Store(ptID, ptBookID int, img []byte) error
}

func (h *Header) DownloadImage(ptID, ptBookID int, uri string, storage ImageStorage) error {
	if !storage.IsExist(ptID, ptBookID) {
		data, err := h.GetFile(uri)
		if err != nil {
			return err
		} else {
			// 转为 jpg 格式并压缩在 MaxImageSize 以内
			buf := bytes.NewBuffer(data)
			img, _, err := image.Decode(buf)
			if err != nil {
				return err
			} else {
				dst := bytes.NewBuffer(nil)
				err = jpeg.Encode(dst, img, &jpeg.Options{Quality: 100 * MaxImageSize / len(data)})
				if err != nil {
					return err
				} else {
					return storage.Store(ptID, ptBookID, dst.Bytes())
				}
			}
		}
	} else {
		return nil
	}
}

func gbkToUtf8(s []byte) ([]byte, error) {
	reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder())
	d, e := ioutil.ReadAll(reader)
	if e != nil {
		return nil, e
	}
	return d, nil
}
